/* FUN WITH BITS PROGRAM */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>  //Include ctype.h so that we can use the isdigit() function to determine if an integer was passed to the program
#include <limits.h> //Include limits.h so that we can get the maximum value of an unsigned integer on the current system (UINT_MAX)
#include <string.h> //Include string.h so that we can use various string functions for creating binary bit strings

//Define constants

//Create a constant for how many bits an unsigned integer is on the current system.
//The "sizeof" function returns how many BYTES a certain variable type takes up on the current system.
//A byte is 8 bits. So we take that value and multiply by 8 to get how many bits we need to print out.
//I'm making it a constant since I use it multiple times in the program and the value never changes.
//Makes the program a little more efficient.
#define UINT_BITS_QTY (sizeof(unsigned int) * 8)

//Create a constant variable for how many bytes an integer has on the current system
#define UINT_BYTES_QTY sizeof(unsigned int)

//Create the prototype for the printBits function
void printBits(unsigned int x);

//Create the prototype for the swapBytes function
unsigned int swapBytes(unsigned int x);

//Create the prototype for the btoi function
unsigned int btoi(char []);


//This is kind of a silly hack but I was trying to figure out how to
//print " x' " instead of just " x " after swapping the bytes since the
//printBits function prints both lines and there was no way to tell it
//which line it was printing and still keep the given function prototype from p1.pdf.
//So I decided to use a "global" variable (meaning you declare it outside
//of all functions and it is accessible to all functions) to be a 0 if we
//aren't swapping bytes or a 1 if we are.
short int areSwapping = 0;


//END Header
//-------------------------------------------------------------------------------------------------------------------
//START btoi Function


//Create the btoi() function.
//Not sure why we had to do this since C already has a function that does that (strtoul())??
//But, I'll do it anyway since that's what we're supposed to do!
unsigned int btoi(char *s){
  
  //Here is the simple way to make this function:
  return strtoul(s,NULL,2);
  
}




//END btoi Function
//-------------------------------------------------------------------------------------------------------------------
//START printBits Function



//Create the printBits function
void printBits(unsigned int x){
  int b,t; //Create variables for looping through the number to print the binary equivalent.
  char xString[3]; //Create variable for printing "x" or "x'" in the output
  
  
  //Create a variable to hold the resulting binary string.
  //Note a string in C is just an array of character variables.
  //Also, there is one ending blank character at the end of all strings
  //so the size of your character array always has to be large enough
  //to hold your entire string PLUS 1 blank character. That's why
  //I add 1 to the UINT_BITS_QTY here.
  char result[UINT_BITS_QTY + 1]; 
  strcpy(result,""); //Clear result string to start fresh
  
  for (b = UINT_BITS_QTY - 1; b >= 0; b--) {
    t = x >> b; //Shifts the number over b many bits
 
    if (t & 1)  //ANDs the resulting number with 1. If t is a 1, then result is 1. If not, then 0
      strcat(result,"1"); 
    else
      strcat(result,"0");
  }
  
  //Print the output which shows the number in decimal, binary, and hex form
  
  //First see if we are printing the swapped bytes or not.
  //If we are, print "x'" instead of "x".
  
  strcpy(xString,""); //Make sure string is cleared out string first
  
  
  //Used a switch statement here just for fun.
  //Could have also just used an if/else statement.
  
  //One note here is I probably didn't need to use a "default"
  //section since the value should always be 0 or 1, but it's
  //usually good policy to always include it just in case there's
  //something you didn't foresee in your program.
  switch(areSwapping){
    case 0:
      strcat(xString,"x");
      break;
    case 1:
      strcat(xString,"x'");
      break;
    default:
      strcat(xString,"x");
      break;
  }
  
  //Print the formatted result.
  //The negative numbers mean to left-align the text.
  //The numbers are the field width.
  printf("\n%-3s%-13u%-36s",xString,x,result);
 
  //Use a separate printf statement to print the hexadecimal conversion.
  //I had to use a separate statement because when I tried to incorporate it
  //into the above printf statement, I was getting weird output. After putting
  //it into a separate statement, everything worked fine.
  printf("0x%08x",x);
  
  //Clear out the result string since a string doesn't really have function scope
  //in C I guess?? Unlike other types of variables in C which when declared in a function
  //usually lose their value after the function ends, strings don't do that in C because
  //of the strange way strings work in C. So, after printBits ends, the results strings still
  //has the binary number in it if you don't specifically clear it out or else it will mess up
  //the calculation and printing the next time printBits is called.
  strcpy(result,"");
  
}



//END printBits Function
//----------------------------------------------------------------------------------------------------------------
//START swapBytes Function



//Create the swapBytes function
unsigned int swapBytes(unsigned int x){
  int b,t,i,c; //Create variables for looping.
  unsigned int s; //Create variable to hold the resulting integer in (after swapping)
  
  //Ok, since we are swapping BYTES here instead of bits, this gets a little tricky.
  //If we were swapping the bits, we would just loop through the number the same ways
  //as in the printBits function, only backwards. That's easy. But, since we are swapping
  //bytes, we need to keep each bit in each byte in the correct order, just swap them around.
  //The way I'm going to do it is create a separate variable for each byte then once I have
  //each byte in there, I will just print them in reverse order. To convert those bytes
  //into an actual integer, I will have to stitch them together using strcat() and then convert
  //them to an integer using strtoul().
  
  //Since the number of bytes is variable (usually 2 or 4 on most systems) I need to create a variable
  //number of variables to hold those bytes (either 2 or 4). But I already know how many I need if I use
  //the UINT_BYTES_QTY variable I declared at the top of this program it will give me exactly how many
  //bytes there are! So I'm going to create an array to hold the bytes that is either 2 or 4 values in size.
  
  //The following creates an array of strings called "byte".
  //byte has "UINT_BYTES_QTY" values (which will usually be 2 or 4).
  //And the "9" indicates that each string in the "byte" array will need 9 characters.
  //The reason we can hard-code 9 here is because we know that every byte has 8 bits.
  //And every string in C always needs to end with an extra "\0" character at the end so we need an extra
  //character. So that becomes 8+1 (9).
  
  /*
  Ok, the following code may look kind of weird. That's because it is!
  Because of the primitive crazy way C uses strings (and arrays) we have
  to create each "byte" variable by allocating memory for it with the malloc()
  function. It took me a while to research to figure out how to get this to work
  and I finally figured it out. I still don't 100% understand it, but I kind of do.
  If you want to know a little more about this part of the code, just ask in an email.
  I don't want to type too much here in the comments if I can help it.
  */
  
  char *byte[UINT_BYTES_QTY];
  for(i = 0; i < UINT_BYTES_QTY; i++){
    byte[i] = malloc(9 *sizeof(char));
  }
  
  
  //Create a variable to hold the resulting binary string.
  //Note a string in C is just an array of character variables.
  //Also, there is one ending blank character at the end of all strings
  //so the size of your character array always has to be large enough
  //to hold your entire string PLUS 1 blank character. That's why
  //I add 1 to the UINT_BITS_QTY here.
  char result[UINT_BITS_QTY + 1];
  
  
  //Ok, to process the bytes we need a semi-complicated loop here.
  
  /*
  I'll try to explain this as briefly as possible, then you can ask questions if you need to.
  What this for-loop is doing is very similar to the one in the printBits function with a few
  modifications. Notice I need 3 counting variables for this to work. Here's what happens:
  (I'm going to use a 32-bit integer for this example since that is what it will be most of the time)
  Ok, I will walk you through the first iteration of this loop to try to show what is happening.
  When this loop starts, b = 31 (UINT_BITS_QTY-1) and obviously i=0 & c=1. When it hits the first
  line, x (which is our integer passed to the function) is shifted to the right 31 bits so that the
  left-most bit is now the right-most bit. Then it's ANDed with 1. If the result is 1, then we know
  the bit is a 1, if it's 0, then we know it's a 0. Whichever it is, then that result is saved into
  the byte[0] string (since i=0 the first time through) using the strcat() function. Then, we get
  to an if-statement that tests c%8 which is just checking to see if we are at the end of a byte,
  which we won't be on the first time through, so it is FALSE and the iteration is done. The next time
  through the loop, b will equal 30, i=0, c=2. The "c" variable is to tell the loop which bit we are
  processing (starting from the left-side). So if c=2, then we are on bit #2. When we get to bit #8,
  then c%8 will be 0 and the if-statement will be true which causes i to be incremented by 1 meaning
  the next time through we will start creating the next byte (which will be byte[1] instead of byte[0]).
  I think that's a long enough explanation for here. I know it wasn't very detailed, so ask if you want
  to know more.
  */
  
  for (b = UINT_BITS_QTY - 1, i = 0, c = 1; b >= 0; b--, c++) {
      t = x >> b; //Shifts the number over b many bits
  
      if (t & 1)  //ANDs the resulting number with 1. If t is a 1, then result is 1. If not, then 0
	strcat(byte[i],"1"); //Add a 1 to the end of the string
      else
	strcat(byte[i],"0"); //Add a 0 to the end of the string
      
      
      //The "c" variable keeps track of how many times we have been through the loop.
      //When it gets to any multiple of 8, we know we have a full byte, so i is incremented
      //so we can start over on the next byte on the next time through the loop.
      //We check that by using modulus (%).
      if( !(c % 8) ){
	//Increment i to move to the next byte
	i++;
      }
  }
  
  //Ok, after the loop, we should have all of our bytes saved in the "bytes" array.
  //Now we stitch all of those bytes back together in another loop so we can make
  //one long binary number in order to convert it back to an unsigned integer for return.
  //We have to use a loop here because the number of bytes is variable depending on the system.
  
  //First, clear the "result" string again so we can reuse it here.
  strcpy(result, "");
  
  //Now loop.
  //Notice we need to stitch them together BACKWARDS because we are swapping them!
  //We can't start at byte[0] because then we would be putting them back together in the
  //same order we started with!
  for(i = UINT_BYTES_QTY - 1; i >= 0; i--){
    strcat(result,byte[i]);
  }
  
  //Convert the binary string created back into an integer value and return it.
  //Note that you MUST use strtoul() here and NOT atoi()! Because atoi() will not
  //convert from a binary string! Only decimal strings. That is what the "2" means
  //is that it should assume the number in the string being converted is a base 2 number.
  s = strtoul(result,NULL,2);
   
  strcpy(result,""); //Clear out the result string since it doesn't seem to clear itself out each time
  
  return s; 
}



//END swapBytes Function
//----------------------------------------------------------------------------------------------------------------
//START main Function



/*
Start the main function. argc is how many command-line
arguments were passed + 1. Meaning if there were 3 command-line
arguments passed, argc would equal 4 because the actual name of the program counts as 1.
argv is an array of the command-line arguments passed. The first value
in the argv array (argv[0]) is the name of the program itself,
so the first actual command-line argument would be argv[1]
*/

int main(int argc, char *argv[]) { 
	
	unsigned int argument; //Create a variable to hold the passed integer in
	int i; //Create variable for looping
	int length; //Create variable which is used for checking if the number entered is too large.
	
	
	
	
	//Print Usage message if there weren't exactly 2 arguments passed
	if(argc != 3){ //This says 2 because the count always includes the program name itself. So 1 argument would equal 2 for argc.
		printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
		return 0; //End Program. No need to go on.
	}
	
	
	//Now that we know that there are 2 command-line arguments,
	//let's make sure that the first one is -i or -b. If it isn't, exit.
	
	//This uses the strcmp() function (means "string compare") to see if 2 strings are the same.
	//This is because C does NOT let you do "if(string1 == string2)" (well, you could do it, but it wouldn't work) so you
	//have to use strcmp() to do that. strcmp() returns 0 if both strings are the same.
	//It returns either a negative or positive number if the 2 strings aren't the same.
	if( strcmp(argv[1],"-i") != 0 && strcmp(argv[1],"-b") != 0 ){
	    printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
		return 0; //End Program. No need to go on.
	}
	
	
	/*
	If we got this far, that means 2 command-line arguments were passed
	and the first one was indeed either -i or -b.
	
	Now we need to test the 2nd command-line argument. If -i was used, we need
	to make sure it's a valid integer and that it isn't larger than maximum.
	If -b was used, we need to make sure it's a string of only 1s and/or 0s and
	that the binary number entered isn't larger than the maximum number allowed.
	
	First we will process -i.
	*/


//---------------------------------------------------------------------------------------------------------------
//START processing -i command-line argument

    if(strcmp(argv[1],"-i") == 0){ //If -i was used
	
      /*
	Now we test to see if it is an integer. This can be tricky in C it seems.
	It is tricky because all command-line arguments are passed to the program
	as strings, even the numbers are passed as strings. The C library doesn't
	provide a function to test a string to see if it contains a valid integer
	so I had to write my own code to do it. There are probably multiple ways
	to do it. The way I did it is to just loop through each character in the string
	and if every character is a digit 0 through 9, then I know it is an integer.
	*/
	
	//Check to make sure the argument is an integer.
	//If it isn't, print the same usage message as above and abort the program
	
	//Loop through argument passed and see if every character is a digit.
	//If it is, we know it's an integer.
	for(i = 0; i < strlen(argv[2]); i++){
		if(!isdigit(argv[2][i])){
			printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
			return 0; //End Program. No need to go on.
		}
	}	
	
	
	
	
	
	//If we got this far, the single argument passed must be an integer. We can now start processing that integer
	
	//Convert passed argument from a string to an unsigned long integer.
	//I had to use the internet to figure out how to correctly use this function.
	//The NULL is used here because the 2nd argument of the strtoul function isn't needed in this situation, so just putting NULL is ok.
	//The "10" means the the string you're converting from should be interpreted as a base-10 number (decimal notation)
	argument = strtoul(argv[2],NULL,10);
	
	/*
	Now that we know the argument is an integer we have one more test to perform.
	We need to see if the entered integer is larger than the maximum allowed.
	Because if it is, it will cause the answer that is outputted to be wrong.
	
	But there is a problem. I'm not sure if it was mentioned in class or not
	but there is NO WAY to simply see if the number passed from the command-line
	is larger than UINT_MAX because IT'S IMPOSSIBLE FOR ANY NUMBER STORED IN A VARIABLE
	TO BE LARGER THAN UINT_MAX BECAUSE THAT IS THE MAXIMUM VALUE!! See the problem?
	So you can't just say if(argument > UINT_MAX) because no value can ever go over that.
	If a value is entered that is larger than UINT_MAX, then any bits past 32 (if the maximum
	is a 32-bit integer) are just truncated and you end up with a number less than equal to
	UINT_MAX. Therefore, I am going to do it the simplest way I can think of which may or may not
	be the "correct" way to do it. We already have the number stored in a string (argv[1]) so we know
	what number was entered. I then will take the number generated after converting it to an unsigned
	integer and convert it back into a string and compare it, character by character, to the original
	number entered and see if it is the same. If the number is the same, then it wasn't too larger
	and didn't overflow the 32-bit maximum value.
	
	I will note that I got the code to convert the integer to a string from the internet since
	C doesn't have a standard library function to convert numbers to strings.
	*/
	
	//Test to make sure the number isn't larger than the maximum allowed value for an unsigned integer
	
	length = (CHAR_BIT * sizeof(argument) - 1) / 3 + 2; //Code to know what size to use in the snprintf function below. I got this code from the internet. Not exactly sure what it does.
	char str[length]; //Define a variable to hold the new string
	snprintf(str, length, "%u", argument); //Converts the argument integer BACK to a string for comparison to the original argument. If it's the same, then the integer was not larger than max.
	
	//Loop Through both strings to see if they are the same. If they aren't, then the number entered was too large. Print Usage info and exit.
	for(i = 0; i < strlen(argv[2]); i++){
		if(str[i] != argv[2][i]){
			printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
			return 0; //End Program. No need to go on.
		}
	}
    
      
    } //End of if(strcmp(argv[1],"-i") == 0) statement
    

//END of processing -i argument
//-----------------------------------------------------------------------------------------------------------------------------
//START processing -b argument
	
	
	
     if(strcmp(argv[1],"-b") == 0){ //If -b was used
       
       //Ok, processing a binary integer string should be easier than a decimal integer string.
       
       //First we make sure all characters are either a 1 or a 0. We'll do this by looping through them.
       //You might be wondering why I didn't use the strcmp() function here like I did above to test if a
       //string equals something?? Well, it's true you can't compare strings to eachother by just using == or !=
       //BUT you CAN compare SINGLE CHARACTERS that way! Just not a string of characters. In this case
       //we are only testing for a single character, either a 1 or a 0, so we don't need strcmp().
       //Notice when comparing with 1 or 0 I used SINGLE QUOTES. This is VERY important! You CAN'T use
       //double quotes!! In C, when you use single quotes around a single character, it actually returns
       //that character's numerical value (usually an ASCII value). Because all characters are stored in memory
       //as integer values, so when you compare a character to another one in C, you need to use single quotes
       //so that the program will convert that character to it's numerical value before comparing it. In ASCII,
       //the numerical value for the 0 character is 48 and the value for 1 is 49. If you do "if( argv[2][i] != '1' )"
       //then the program converts '1' to 49 and then compares that value to what is in memory for argv[2][i] which will
       //also be 49 if it's a 1. BUT if you wrote "if( argv[2][i] != "1" )" with double quotes, then it will literally
       //try to compare a "1" to what is in memory. And, of course, 1 does not equal 49. This code would also work if
       //you just replaced the '0' with 48 and the '1' with 49, but it makes it easier to see what the code is doing
       //if you leave it the way it is.
       for(i = 0; i < strlen(argv[2]); i++){
		if( argv[2][i] != '1' && argv[2][i] != '0'){
			printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
			return 0; //End Program. No need to go on.
		}
	}
	
	
	//Ok, now that we know that only 1s and 0s were entered we need to make sure the binary number entered
	//isn't larger than the maximum integer value allowed. This might sound hard to do when you first think
	//about it, but it's actually VERY SIMPLE! WAY easier than checking if a decimal integer is over max!!!
	
	//To understand what makes it simple, just think about what the maximum allowed integer means. The maximum
	//allowed integer size is determined by what? BY HOW MANY BITS CAN BE USED TO HOLD THAT INTEGER!! Which is
	//usually 16 or 32 bits on most computers. Therefore, all we need to do is count how many bits were entered!
	//We compare the string length of the binary number entered and if it is less-than or equal to UINT_BITS_QTY,
	//then it can't be greater than maximum! VERY EASY!
	
	//UPDATE - 9/23/14: I forgot about accounting for leading zeros in a binary number. Really, it's ok if somebody
	//enters a 36-bit binary number on a system with a 32-bit max if at least the first 4 bits are zeros because
	//they wouldn't add to the total value. So, what this check should really do is start counting the bits from
	//left to right FROM THE FIRST 1 in the entered value. So I will loop through the digits first and as soon
	//as the first 1 is encountered, it will subtract all the zeros encountered (if any) off the total bit count
	//before validationg the input.
	
	for(i = 0; i < strlen(argv[2]); i++){ //Loop through the input and find the first 1
	    if( argv[2][i] == '1' ){
		break;
	    }
	}
	
	//UPDATED COMMENT - 9/23/14
	//Ok, now that we looped through the input looking for the first 1, we can just
	//use the "i" variable to let us know how many leading zeros there were in the input.
	//I know "i" is used many times in this program, but since this takes place directly
	//after the loop above, it should be set to the number of leading zeros at this point in the program
	if( (strlen(argv[2]) - i) > UINT_BITS_QTY ){ //If binary string is larger than maximum
	    printf("\nUsage: endian -i|-b <x>\n-i x: Positive integer less than %u\n-b x: Binary representation of positive integer less than %u\n      (string of 1s and 0s)\n\n",UINT_MAX,UINT_MAX);
	    return 0; //End Program. No need to go on.
	}
	
	//THAT'S IT!
	
	
	//Now that the binary string has been authenticated, we can turn it into an integer for output!
	//We will use our custom function btoi() because that's what p1.pdf said to do.
	argument = btoi(argv[2]);
	
       
     } //End of if(strcmp(argv[1],"-b") == 0) statement
	
	
      //Now we FINALLY start the bit manipulation! Sheesh! That took WAY too long to get to this point! C is too primitive!
	
	//Print the results header
	printf("%-14s%-36s%-11s\n----------------------------------------------------------------","Decimal","Binary","Hexadecimal");
	
	
	//Print the first line of the output which is the entered number converted to Binary and Hex
	printBits(argument);
		
	//Print the second line of the output which is the bits swapped
	areSwapping = 1; //Set the areSwapping flag so our printBits function will print "x'" instead of "x"
	printBits(swapBytes(argument));
	areSwapping = 0; //Reset areSwapping flag
	
	
	
	
	
	printf("\n"); //Add blank line at the end just for looks
	
	return 0;
}